Перейти к основному содержимому

5.10. Справочник по Go

Разработчику Архитектору

Справочник по Go

1. Основные синтаксические элементы

1.1. Комментарии

  • Однострочные комментарии начинаются с //.
  • Многострочные комментарии заключаются между /* и */.
  • Комментарии, начинающиеся с //go:, интерпретируются как директивы компилятора (например, //go:noinline).

1.2. Идентификаторы

  • Идентификаторы состоят из букв, цифр и символа подчеркивания _.
  • Первый символ не может быть цифрой.
  • Идентификаторы чувствительны к регистру.
  • Идентификаторы, начинающиеся с заглавной буквы, экспортируются из пакета (публичный доступ).
  • Идентификаторы, начинающиеся со строчной буквы, имеют внутреннюю видимость (приватный доступ).

1.3. Ключевые слова

Go содержит 25 ключевых слов:

break     default      func     interface    select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

Эти слова нельзя использовать в качестве идентификаторов.

1.4. Предопределённые идентификаторы

Следующие идентификаторы предопределены, но не являются ключевыми словами:

bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string uint uint8
uint16 uint32 uint64 uintptr true false iota nil

Также предопределены функции:

  • append, cap, close, complex, copy, delete, imag, len, make, new, panic, print, println, real, recover.

Эти идентификаторы можно переопределять в локальной области видимости, но это не рекомендуется.


2. Типы данных

2.1. Базовые типы

Логический тип

  • bool: значения true и false.

Числовые типы

Целочисленные:

  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64, uintptr

Псевдонимы:

  • byte — псевдоним для uint8
  • rune — псевдоним для int32 (представляет Unicode-кодовую точку)

Вещественные:

  • float32, float64

Комплексные:

  • complex64 — пара float32
  • complex128 — пара float64

Строковый тип

  • string: неизменяемая последовательность байтов в кодировке UTF-8.
  • Длина строки определяется функцией len(s), возвращающей количество байтов.
  • Для получения количества Unicode-символов используется utf8.RuneCountInString(s).

2.2. Составные типы

Указатели

  • *T — указатель на значение типа T.
  • Оператор & получает адрес переменной.
  • Оператор * разыменовывает указатель.
  • Указатели на нулевые значения равны nil.

Массивы

  • [n]T — массив фиксированной длины n элементов типа T.
  • Длина массива является частью его типа.
  • Массивы передаются по значению (копируются при присваивании или передаче в функцию).

Срезы (slices)

  • []T — срез элементов типа T.
  • Срез содержит указатель на массив, длину (len) и вместимость (cap).
  • Создаются через литералы, функцию make, или срезание массива/среза.
  • Пример: s := []int{1, 2, 3}

Карты (maps)

  • map[K]V — ассоциативный массив с ключами типа K и значениями типа V.
  • Ключи должны быть сравнимыми (не содержать срезы, карты, функции).
  • Создаются через литералы или make(map[K]V).
  • Чтение по отсутствующему ключу возвращает нулевое значение и false во втором возвращаемом значении.

Структуры

  • struct — коллекция полей с именами.
  • Поля могут быть экспортированы (заглавная буква) или приватными.
  • Анонимные поля (встраивание) позволяют наследовать поведение.

Функции

  • Тип функции определяется сигнатурой: списком параметров и возвращаемых значений.
  • Функции являются значениями первого класса и могут присваиваться переменным, передаваться как аргументы, возвращаться из других функций.

Интерфейсы

  • interface{} — пустой интерфейс, совместимый с любым типом.
  • Непустой интерфейс определяет набор методов.
  • Реализация интерфейса неявная: если тип реализует все методы интерфейса, он удовлетворяет ему.

Каналы

  • chan T — канал для передачи значений типа T.
  • Создаются через make(chan T).
  • Поддерживают операции отправки (ch <- value), получения (<-ch), закрытия (close(ch)).
  • Могут быть однонаправленными: chan<- T (только отправка), <-chan T (только получение).

3. Константы и переменные

3.1. Объявление переменных

  • Полная форма: var name Type = value
  • Краткая форма (внутри функций): name := value
  • Групповое объявление:
    var (
    a int = 1
    b string
    )

3.2. Константы

  • Объявляются с помощью const.
  • Значения вычисляются на этапе компиляции.
  • Поддерживают iota — счётчик, начинающийся с 0 внутри блока const.
  • Пример:
    const (
    Red = iota // 0
    Green // 1
    Blue // 2
    )

3.3. Нулевые значения

Каждый тип имеет нулевое значение:

  • boolfalse
  • числовые типы → 0
  • string""
  • указатели, срезы, карты, каналы, функции, интерфейсы → nil

4. Управляющие конструкции

4.1. Условия

  • if condition { ... }
  • if init; condition { ... } — инициализация перед условием (переменная видна только внутри блока if и else)
  • else и else if используются стандартно.

4.2. Циклы

  • Единственный цикл в Go — for.
  • Формы:
    • for init; condition; post { ... }
    • for condition { ... } — аналог while
    • for { ... } — бесконечный цикл
    • for key, value := range collection { ... } — итерация по массивам, срезам, строкам, карте, каналам

4.3. Переключатель (switch)

  • switch expr { case val1: ... default: ... }
  • Поддерживает выражения в case.
  • Автоматический выход из case (без проваливания, в отличие от C).
  • Для проваливания используется fallthrough.
  • Можно использовать без выражения: switch { case cond1: ... } — аналог цепочки if-else.

4.4. Выбор для каналов (select)

  • select { case ch <- v: ... case x := <-ch2: ... default: ... }
  • Блокирует выполнение до готовности одного из каналов.
  • default делает выбор неблокирующим.
  • Если несколько каналов готовы, выбирается случайный.

5. Функции

5.1. Объявление

func name(param1 Type1, param2 Type2) (ret1 RetType1, ret2 RetType2) {
// тело
return value1, value2
}

5.2. Особенности

  • Могут возвращать несколько значений.
  • Именованные возвращаемые значения инициализируются нулевыми значениями.
  • Использование return без аргументов возвращает текущие значения именованных переменных.
  • Отложенные вызовы (defer) выполняются после завершения функции, в порядке LIFO.

5.3. Анонимные функции и замыкания

  • Функции могут быть определены внутри других функций.
  • Замыкания захватывают переменные по ссылке.

5.4. Вариадические функции

  • Последний параметр может быть объявлен как ...Type.
  • Внутри функции доступен как срез []Type.
  • Пример: func sum(nums ...int) int

6. Методы и интерфейсы

6.1. Методы

  • Объявляются с получателем: func (r ReceiverType) MethodName(...) ...
  • Получатель может быть значением (T) или указателем (*T).
  • Методы с указателем-получателем могут изменять состояние.

6.2. Интерфейсы

  • Интерфейс определяет поведение через методы.
  • Пустой интерфейс interface{} принимает любой тип.
  • Начиная с Go 1.18, поддерживаются параметризованные интерфейсы (в рамках дженериков).
  • Проверка реализации: var _ MyInterface = (*MyType)(nil)

6.3. Type assertion и type switch

  • value, ok := someInterface.(ConcreteType)
  • ok равно true, если преобразование возможно.
  • Type switch:
    switch v := x.(type) {
    case int:
    // v имеет тип int
    case string:
    // v имеет тип string
    }

7. Пакеты и модули

7.1. Пакеты

  • Каждый файл начинается с package name.
  • Имя исполняемого пакета — main.
  • Экспорт осуществляется через заглавные имена.
  • Импорт: import "path/to/package"
  • Групповой импорт:
    import (
    "fmt"
    "os"
    )

7.2. Модули

  • Управление зависимостями через go mod.
  • Команды:
    • go mod init <module> — инициализация
    • go mod tidy — синхронизация зависимостей
    • go.mod — файл описания модуля
    • go.sum — контрольные суммы зависимостей

8. Встроенные функции

Go предоставляет набор предопределённых функций, доступных без импорта:

8.1. make

  • Создаёт срезы, карты и каналы.
  • Синтаксис:
    • make([]T, length, capacity) — срез
    • make(map[K]V, initialCapacity) — карта
    • make(chan T, bufferSize) — канал
  • Возвращает инициализированный экземпляр, а не указатель.

8.2. new

  • Выделяет память под значение указанного типа.
  • Возвращает указатель на нулевое значение типа: *T.
  • Эквивалентно var t T; return &t.

8.3. len и cap

  • len(v) возвращает длину:
    • массива — количество элементов
    • среза — количество элементов
    • строки — количество байтов
    • карты — количество пар ключ-значение
    • канала — количество элементов в буфере
  • cap(v) возвращает вместимость:
    • среза — максимальное количество элементов без реаллокации
    • канала — размер буфера

8.4. append

  • Добавляет элементы к срезу: s = append(s, elem...)
  • Может выделить новый массив, если текущая вместимость исчерпана.
  • Поддерживает распаковку другого среза через ...: append(s1, s2...)

8.5. copy

  • Копирует элементы из одного среза в другой: n := copy(dst, src)
  • Возвращает количество скопированных элементов (минимум из len(dst), len(src)).
  • Работает только со срезами одинакового базового типа.

8.6. delete

  • Удаляет пару ключ-значение из карты: delete(m, key)
  • Безопасна для вызова с несуществующим ключом.

8.7. close

  • Закрывает канал: close(ch)
  • После закрытия все последующие операции получения возвращают нулевое значение.
  • Отправка в закрытый канал вызывает панику.
  • Только отправляющая сторона должна закрывать канал.

8.8. panic и recover

  • panic(v) немедленно останавливает выполнение функции и начинает раскрутку стека.
  • recover() останавливает раскрутку стека и возвращает значение, переданное в panic.
  • recover работает только внутри отложенной (defer) функции.

8.9. Комплексные числа

  • complex(real, imag) — создаёт комплексное число.
  • real(c) — возвращает вещественную часть.
  • imag(c) — возвращает мнимую часть.

8.10. print и println

  • Низкоуровневые функции вывода в stderr.
  • Используются только в отладочных целях или при отсутствии стандартной библиотеки.
  • Не рекомендуются для production-кода.

9. Обработка ошибок

9.1. Тип error

  • Встроенный интерфейс: type error interface { Error() string }
  • Ошибки возвращаются как обычные значения, обычно последним аргументом.
  • Проверка:
    if err != nil {
    // обработка
    }

9.2. Создание ошибок

  • errors.New("message") — простая ошибка.
  • fmt.Errorf("format %v", value) — форматированная ошибка.
  • Начиная с Go 1.13:
    • fmt.Errorf("...: %w", err) — оборачивает ошибку (wrapping)
    • errors.Is(err, target) — проверяет цепочку ошибок на наличие target
    • errors.As(err, &target) — пытается преобразовать ошибку к типу target

9.3. Пользовательские ошибки

  • Реализация интерфейса error:
    type MyError struct { Msg string; Code int }
    func (e MyError) Error() string { return e.Msg }

9.4. Отказ от игнорирования ошибок

  • Идиоматический код всегда проверяет ошибки.
  • Игнорирование через _ допустимо только в обоснованных случаях.

10. Конкурентность

10.1. Горутины

  • Лёгкие потоки, управляемые планировщиком Go.
  • Запускаются с помощью ключевого слова go:
    go func() { ... }()
  • Расход памяти на горутину — от 2 КБ.
  • Планировщик использует M:N-модель (M горутин на N системных потоков).

10.2. Каналы

  • Основной механизм коммуникации между горутинами.
  • Буферизованные каналы: ch := make(chan int, 10)
  • Небуферизованные каналы: синхронизируют отправителя и получателя.
  • Закрытие канала сигнализирует об окончании передачи данных.

10.3. Select

  • Позволяет ждать несколько операций с каналами.
  • Поддерживает default для неблокирующего поведения.
  • Используется для таймаутов, отмены, объединения потоков.

10.4. sync-примитивы

Пакет sync предоставляет:

  • Mutex и RWMutex — блокировки для защиты общих данных.
  • WaitGroup — ожидание завершения группы горутин.
  • Once — гарантирует однократное выполнение функции.
  • Cond — условная переменная для координации.
  • Pool — пул временных объектов для снижения нагрузки на сборщик мусора.

10.5. Context

Пакет context обеспечивает:

  • Отмену операций (WithCancel)
  • Таймауты (WithTimeout)
  • Дедлайны (WithDeadline)
  • Передачу значений (WithValue)
  • Широко используется в HTTP-серверах, базах данных, gRPC.

Идиома:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

11. Инструменты разработки

11.1. Компилятор и сборка

  • go build — компиляция в исполняемый файл.
  • go run — компиляция и запуск в одном шаге.
  • go install — компиляция и установка в $GOBIN.

11.2. Форматирование

  • gofmt — стандартный инструмент форматирования.
  • go fmt — применяет gofmt ко всем файлам пакета.

11.3. Линтинг и анализ

  • go vet — статический анализ на распространённые ошибки.
  • staticcheck, golangci-lint — сторонние линтеры.

11.4. Тестирование

  • Файлы с _test.go.
  • Функции: TestXxx(t *testing.T), BenchmarkXxx(b *testing.B), ExampleXxx().
  • Запуск: go test
  • Покрытие: go test -cover

11.5. Профилирование

  • Пакет net/http/pprof — профилирование CPU, памяти, горутин.
  • Генерация профилей: go tool pprof binary profile.out

11.6. Документация

  • godoc (устаревший) или pkg.go.dev.
  • Комментарии над объявлением становятся документацией.
  • Генерация: go doc <package>

12. Стандартная библиотека (выборочно)

ПакетНазначение
fmtФорматированный ввод/вывод
strconvПреобразование строк и чисел
strings, bytesРабота с текстом и байтами
io, bufioАбстракции ввода-вывода
os, path/filepathРабота с файловой системой
net/httpHTTP-клиент и сервер
encoding/json, xmlСериализация данных
timeРабота с датой и временем
regexpРегулярные выражения
crypto/*Криптографические функции
database/sqlУнифицированный интерфейс к БД
flagРазбор аргументов командной строки
logПростое логирование

13. Дженерики (Generics)

Дженерики были введены в Go 1.18 и позволяют писать переиспользуемый код без привязки к конкретному типу.

13.1. Объявление параметризованных функций

func Print[T any](s []T) {
for _, v := range s {
fmt.Println(v)
}
}
  • T — параметр типа.
  • any — псевдоним для interface{} (ограничение, допускающее любой тип).

13.2. Ограничения типов (type constraints)

  • Можно задавать интерфейсы как ограничения:
    type Number interface {
    int | int32 | int64 | float32 | float64
    }

    func Sum[T Number](a, b T) T {
    return a + b
    }
  • Стандартные ограничения: comparable (для типов, поддерживающих == и !=).

13.3. Параметризованные типы

type Stack[T any] struct {
items []T
}

func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}

13.4. Использование в стандартной библиотеке

  • Начиная с Go 1.21, в slices, maps, cmp появились дженерик-функции:
    • slices.Contains, slices.SortFunc
    • maps.Keys, maps.Clone
    • cmp.Compare

13.5. Ограничения и рекомендации

  • Дженерики не заменяют интерфейсы.
  • Избегайте избыточного использования — применяйте только при реальной необходимости переиспользования логики.
  • Дженерики не влияют на производительность: специализация происходит на этапе компиляции.

14. Рефлексия

Пакет reflect позволяет анализировать типы и значения во время выполнения.

14.1. Основные типы

  • reflect.Type — метаинформация о типе.
  • reflect.Value — значение с возможностью чтения/записи.

14.2. Получение информации

v := reflect.ValueOf(x)
t := v.Type()

14.3. Возможности

  • Чтение и запись полей структуры.
  • Вызов методов по имени.
  • Создание новых значений: reflect.New(t), reflect.MakeSlice, reflect.MakeMap.
  • Проверка возможности установки: v.CanSet().

14.4. Производительность

  • Рефлексия медленнее прямого кода.
  • Используется в сериализаторах (json, xml), ORM, тестовых фреймворках.
  • Кэширование reflect.Type улучшает производительность.

14.5. Ограничения

  • Нельзя получить исходный идентификатор переменной.
  • Нельзя вызвать неэкспортированные методы из другого пакета.
  • Работа с указателями требует разыменования.

15. Работа с файловой системой

15.1. Основные операции (пакет os)

  • os.Create(name) — создаёт файл.
  • os.Open(name) — открывает файл только для чтения.
  • os.OpenFile(name, flag, perm) — гибкое открытие с флагами (O_RDONLY, O_WRONLY, O_CREATE, O_APPEND и т.д.).
  • os.Remove(name) — удаляет файл.
  • os.RemoveAll(path) — рекурсивное удаление директории.

15.2. Чтение и запись

  • io.ReadFull, io.ReadAll, io.Copy — удобные утилиты.
  • bufio.Scanner — построчное чтение.
  • os.WriteFile, os.ReadFile — чтение/запись всего файла за раз.

15.3. Информация о файле

  • fi, err := os.Stat(path) — получает fs.FileInfo.
  • Поля: Name(), Size(), Mode(), ModTime(), IsDir().

15.4. Путь и навигация

  • filepath.Join, filepath.Dir, filepath.Base, filepath.Ext
  • filepath.Walk — рекурсивный обход дерева каталогов.

15.5. Временные файлы

  • os.CreateTemp(dir, pattern) — безопасное создание временного файла.
  • ioutil.TempDir (устарело, заменено на os.MkdirTemp).

16. Сетевое программирование

16.1. Низкоуровневые сокеты (net)

  • net.Dial("tcp", "host:port") — клиентское соединение.
  • net.Listen("tcp", ":8080") — серверное прослушивание.
  • Поддержка TCP, UDP, Unix-сокетов.

16.2. HTTP-сервер

http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
http.ListenAndServe(":8080", nil)

16.3. HTTP-клиент

  • http.Get, http.Post — простые запросы.
  • http.Client — настраиваемый клиент с таймаутами, куками, прокси.
  • Поддержка HTTPS, редиректов, keep-alive.

16.4. Middleware и маршрутизация

  • Стандартный http.ServeMux поддерживает базовую маршрутизацию.
  • Популярные сторонние маршрутизаторы: gorilla/mux, chi, gin, echo.

16.5. WebSocket, gRPC, GraphQL

  • golang.org/x/net/websocket — официальный (упрощённый) WebSocket.
  • google.golang.org/grpc — реализация gRPC.
  • Сторонние библиотеки для GraphQL: graphql-go, gqlgen.

17. Сборка и развёртывание

17.1. Кросскомпиляция

  • Установка переменных окружения:
    GOOS=linux GOARCH=amd64 go build -o app-linux main.go
  • Поддерживаемые ОС и архитектуры: go tool dist list

17.2. Флаги компилятора

  • -ldflags="-s -w" — убирает отладочную информацию (уменьшает размер).
  • -trimpath — удаляет пути к исходникам из бинарника.
  • -buildvcs=false — отключает внедрение информации о VCS.

17.3. Статическая линковка

  • По умолчанию бинарники статически скомпонованы (кроме CGO).
  • Для CGO: CGO_ENABLED=0 — отключает CGO, обеспечивает полную статическую сборку.

17.4. Docker-образы

  • Минимальный образ: FROM scratch или FROM alpine.
  • Многоступенчатая сборка:
    FROM golang:1.23 AS builder
    WORKDIR /app
    COPY . .
    RUN go build -o myapp .

    FROM scratch
    COPY --from=builder /app/myapp /myapp
    ENTRYPOINT ["/myapp"]

17.5. Версионирование

  • Использование go.mod с семантическим версионированием.
  • Теги в Git: v1.2.3 — автоматически распознаются как версии модуля.

18. Производительность и оптимизация

18.1. Аллокации

  • Минимизация выделения памяти в горячем коде.
  • Использование пулов (sync.Pool) для временных объектов.
  • Предварительное выделение срезов: make([]T, 0, capacity)

18.2. Профилирование

  • CPU profile: pprof.StartCPUProfile
  • Heap profile: pprof.WriteHeapProfile
  • Goroutine profile: /debug/pprof/goroutine

18.3. Escape analysis

  • Компилятор определяет, где хранить переменную — в стеке или куче.
  • Флаг -gcflags="-m" показывает результат анализа.

18.4. Benchmarking

  • Написание бенчмарков:
    func BenchmarkAppend(b *testing.B) {
    for i := 0; i < b.N; i++ {
    _ = append([]int{}, 1)
    }
    }
  • Запуск: go test -bench=.

18.5. Конкурентность vs параллелизм

  • Горутины не гарантируют параллельное выполнение.
  • Установка GOMAXPROCS(n) управляет числом системных потоков.
  • По умолчанию GOMAXPROCS равно числу логических CPU.

19. Архитектурные рекомендации

19.1. Структура проекта

Рекомендуемая структура (в соответствии с Standard Go Project Layout):

/cmd/
/myapp/
main.go
/internal/
/handlers/
/services/
/repositories/
/pkg/
/utils/
/validators/
/api/
/proto/
/openapi/
/config/
/docs/
/scripts/
/test/
/go.mod

19.2. Разделение ответственности

  • main — только инициализация.
  • Бизнес-логика — в /internal.
  • Переиспользуемые компоненты — в /pkg.
  • Конфигурация — через флаги, переменные окружения или конфиг-файлы.

19.3. Обработка ошибок

  • Централизованное логирование ошибок.
  • Использование контекста для отмены.
  • Оборачивание ошибок с контекстом: fmt.Errorf("failed to connect: %w", err)

19.4. Тестирование

  • Таблицы тестов (test cases).
  • Моки через интерфейсы.
  • Интеграционные тесты с testcontainers или dockertest.

19.5. Логирование

  • Использование структурированного логгера: zap, log/slog (Go 1.21+).
  • Избегание fmt.Println в production.

20. Переменные окружения и конфигурация

20.1. Чтение

  • os.Getenv("KEY") — получение значения.
  • os.LookupEnv("KEY") — проверка существования.

20.2. Библиотеки

  • github.com/spf13/viper — поддержка JSON, YAML, TOML, env, флагов.
  • github.com/kelseyhightower/envconfig — привязка env-переменных к структуре.

20.3. Безопасность

  • Не логировать чувствительные переменные (пароли, токены).
  • Использовать .env файлы только в разработке.

21. Работа с базами данных

21.1. Пакет database/sql

  • Стандартный интерфейс для реляционных баз данных.
  • Требует драйвера (например, github.com/lib/pq для PostgreSQL, github.com/go-sql-driver/mysql для MySQL).

21.2. Подключение

db, err := sql.Open("postgres", "user=... password=... dbname=...")
if err != nil { /* handle */ }
defer db.Close()
  • sql.Open не устанавливает соединение немедленно — проверка через db.Ping().

21.3. Выполнение запросов

  • db.Query — выборка (SELECT)
  • db.Exec — модификация (INSERT, UPDATE, DELETE)
  • db.Prepare — подготовленные выражения (повторное использование)

21.4. Обработка результатов

rows, _ := db.Query("SELECT id, name FROM users")
defer rows.Close()
for rows.Next() {
var id int
var name string
rows.Scan(&id, &name)
}

21.5. Транзакции

tx, _ := db.Begin()
_, _ = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
_, _ = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
tx.Commit() // или tx.Rollback()

21.6. ORM и query builders

  • GORM — полнофункциональный ORM с поддержкой ассоциаций, предзагрузки, миграций.
  • SQLBoiler, Ent, Squirrel — альтернативы с разным уровнем абстракции.
  • Рекомендация: начинать с database/sql, переходить к ORM при сложной доменной модели.

22. Миграции баз данных

22.1. Инструменты

  • Goose: поддержка SQL и Go-миграций, откаты.
  • Flyway (через CLI), Liquibase — внешние инструменты.
  • GORM AutoMigrate — автоматическая синхронизация схемы (не для production).

22.2. Структура миграции (Goose)

-- +goose Up
CREATE TABLE users (...);

-- +goose Down
DROP TABLE users;

22.3. Применение

goose -dir migrations postgres "..." up

22.4. Best practices

  • Каждая миграция — атомарна.
  • Никогда не изменять применённую миграцию.
  • Использовать транзакции там, где это поддерживается СУБД.

23. Валидация входных данных

23.1. Встроенные средства

  • Проверка через условия: if len(name) == 0 { ... }
  • Использование errors.New или fmt.Errorf.

23.2. Библиотеки

  • go-playground/validator — теги структур:
    type User struct {
    Email string `validate:"required,email"`
    Age int `validate:"gte=0,lte=130"`
    }
  • Поддержка кастомных валидаторов, локализации ошибок.

23.3. HTTP-валидация

  • Валидация тела запроса после декодирования JSON.
  • Возврат ошибок в формате JSON с кодом 400 Bad Request.

24. CLI-приложения

24.1. Стандартные средства

  • flag — простой парсер флагов.
  • os.Args — прямой доступ к аргументам.

24.2. Расширенные библиотеки

  • urfave/cli — подкоманды, флаги, help-система.
  • cobra — мощный фреймворк (используется в Kubernetes, Helm).
  • Поддержка автодополнения, конфигурационных файлов, версионирования.

24.3. Пример (cobra)

var rootCmd = &cobra.Command{
Use: "myapp",
Short: "A brief description",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello")
},
}

24.4. Best practices

  • Чёткая документация через --help.
  • Использование exit code 0 при успехе, ненулевой — при ошибках.
  • Логирование в stderr, данные — в stdout.

25. Обработка сигналов операционной системы

25.1. Пакет os/signal

sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan // ожидание сигнала

25.2. Graceful shutdown

  • Остановка HTTP-сервера:
    srv := &http.Server{Addr: ":8080", Handler: handler}
    go func() {
    <-sigChan
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    srv.Shutdown(ctx)
    }()
    srv.ListenAndServe()

25.3. Поддерживаемые сигналы

  • os.Interrupt — Ctrl+C
  • syscall.SIGTERM — завершение от менеджера процессов (systemd, Docker)
  • syscall.SIGHUP — перезагрузка конфигурации (опционально)

26. Работа с временными зонами

26.1. Тип time.Time

  • Хранит время в UTC, но содержит информацию о временной зоне.
  • time.Now() — текущее локальное время.
  • time.Now().UTC() — текущее время в UTC.

26.2. Загрузка временных зон

  • time.LoadLocation("Europe/Moscow")
  • Использование: t.In(loc)

26.3. Форматирование

  • Константы: time.RFC3339, time.ANSIC
  • Кастомный формат: "2006-01-02 15:04:05 MST"

Примечание: Go использует фиксированную дату Mon Jan 2 15:04:05 MST 2006 как шаблон.

26.4. Парсинг

  • time.Parse(layout, value) — парсинг строки.
  • time.ParseInLocation(layout, value, loc) — с учётом временной зоны.

27. Юнит-тестирование с моками

27.1. Подход через интерфейсы

  • Зависимости объявляются как интерфейсы.
  • Реализация — в production, мок — в тестах.

27.2. Генерация моков

  • GoMock (gomock): генерация моков из интерфейсов.
  • Testify/mock: ручное или полуавтоматическое создание.

27.3. Пример (Testify)

type MockDB struct {
mock.Mock
}

func (m *MockDB) GetUser(id int) (User, error) {
args := m.Called(id)
return args.Get(0).(User), args.Error(1)
}

// В тесте:
db := new(MockDB)
db.On("GetUser", 1).Return(User{Name: "Alice"}, nil)

27.4. Assertion

  • testify/assert: assert.Equal(t, expected, actual)
  • testify/require: останавливает тест при провале.

28. CI/CD для проектов на Go

28.1. GitHub Actions

Пример workflow:

name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: '1.23' }
- run: go test -v ./...

28.2. Проверки

  • go vet
  • gofmt -s -l . (проверка форматирования)
  • go mod tidy && git diff --exit-code go.mod go.sum
  • staticcheck ./...

28.3. Публикация

  • Сборка бинарников для разных ОС/архитектур.
  • Прикрепление артефактов к релизам.
  • Публикация модуля через Git-теги (v1.2.3).

29. Лицензирование

29.1. Выбор лицензии

  • MIT — минималистичная, разрешающая.
  • Apache 2.0 — с явным указанием авторства и патентной защитой.
  • GPL — требует открытости производных работ (редко используется в Go-экосистеме).

29.2. Указание в проекте

  • Файл LICENSE в корне.
  • Комментарий в go.mod: // license MIT

29.3. Проверка зависимостей

  • go list -m -json all | jq '.[].Indirect' — анализ косвенных зависимостей.
  • Инструменты: license-checker, scancode.

30. Паттерны проектирования в Go

30.1. Options Pattern

Гибкая настройка структур:

type Server struct { Port int; TLS bool }

type Option func(*Server)

func WithPort(p int) Option { return func(s *Server) { s.Port = p } }

func NewServer(opts ...Option) *Server {
s := &Server{Port: 8080}
for _, opt := range opts {
opt(s)
}
return s
}

30.2. Builder Pattern

Построение сложных объектов шаг за шагом.

30.3. Middleware Pattern

Цепочка обработчиков:

type Middleware func(http.Handler) http.Handler

30.4. Repository Pattern

Абстрагирование доступа к данным:

  • Интерфейс UserRepository
  • Реализация PostgresUserRepository

30.5. Dependency Injection

  • Ручная передача зависимостей.
  • Использование контейнеров: dig, wire (Google).

31. Антипаттерны и распространённые ошибки

  • Возврат указателя на локальную переменную в цикле.
  • Игнорирование ошибок (_ = f()).
  • Захват переменной цикла в замыкании без копирования.
  • Передача мьютекса по значению.
  • Использование глобального состояния без синхронизации.
  • Избыточное использование рефлексии и дженериков.
  • Отсутствие контекста в длительных операциях.